﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace Mindless
{
    public partial class Program
    {
        const string MasterFile = @"C:\Program Files\KSetback\KSetback.msg.master";
        const string SlaveFile = @"C:\Program Files\KSetback\KSetback.msg.slave";

        enum InputKey
        {
            Initialization = 1,
            StartNewGame,
            DealNewHand,
            FirstThreeCardsOfHand,
            FinalThreeCardsOfHand,
            BiddingAuction,
            EndOfBiddingAuction,
            StartOfTrick,
            CardInTrick,
            EndOfTrick,
            EndOfHand,
            EndOfGame
        };

        static List<int> ReadMasterRecord()
        {
                // wait for the master file to become available
            FileInfo info;
            StreamReader rdr;
            for (;;)
            {
                info = new FileInfo(MasterFile);
                if (info.Exists)
                {
                    try
                    {
                            // if we can open the file for writing, then the master is done with it
                        rdr = info.OpenText();
                        break;
                    }
                    catch (IOException)
                    {
                        WriteLine("Waiting for master file");
                    }
                }
                System.Threading.Thread.Sleep(100);
            }

            List<int> result;
            using (rdr)
            {
                string[] tokens = rdr.ReadLine().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                result = new List<string>(tokens).ConvertAll<int>(int.Parse);
            }
            info.Delete();

            return result;
        }

        static void WriteSlaveRecord(List<int> record)
        {
            using (TextWriter wtr = new StreamWriter(SlaveFile, false))
            {
                record.ForEach(delegate(int i) { wtr.Write(" {0}", i); });
                wtr.WriteLine();
            }
        }

        static void WriteSlaveRecord(InputKey key, params int[] values)
        {
            List<int> record = new List<int>();
            record.Add((int) key + 100);
            record.AddRange(values);
            WriteSlaveRecord(record);
        }

        static int ConvertSeat(int iSeat)
        {
            return (iSeat + 1) % Settings.PlayerCount;   // KSetback uses W, N, E, S
        }

        static void PlayKSetback()
        {
                // initialize communication
            List<int> input = ReadMasterRecord();
            Trace.Assert(input[0] == (int) InputKey.Initialization);
            WriteSlaveRecord(InputKey.Initialization, (input[1] == 0) && (input[2] == 5) ? 0 : -1);   // we are E+W, goal is 11 points, no smudge, allow slide-out

                // play games until KSetback says to stop
            while (ReadMasterRecord()[0] == (int) InputKey.StartNewGame)
            {
                    // start a new game
                WriteSlaveRecord(InputKey.StartNewGame, 0);
                Game game = new Game();
                while (!game.IsOver)
                {
                    Display(game);

                        // get ready to accept the cards dealt
                    input = ReadMasterRecord();
                    int iFirstBidder = ConvertSeat((input[1] + 1) % Settings.PlayerCount);   // shift from dealer to first bidder
                    Trace.Assert(input[0] == (int) InputKey.DealNewHand);
                    Trace.Assert(input[2] == game.Score[1]);
                    Trace.Assert(input[3] == game.Score[0]);
                    List<IList<Card>> hands = new List<IList<Card>>(Settings.PlayerCount);
                    for (int iSeat = 0; iSeat < Settings.PlayerCount; ++iSeat)
                        hands.Add(new List<Card>(Settings.HandSize));
                    WriteSlaveRecord(InputKey.DealNewHand, 0);

                        // cards are dealt in two chunks of three
                    foreach (InputKey key in new InputKey[] { InputKey.FirstThreeCardsOfHand, InputKey.FinalThreeCardsOfHand })
                    {
                            // each player accepts a chunk
                        for (int iPlayer = 0; iPlayer < Settings.PlayerCount; ++iPlayer)
                        {
                            input = ReadMasterRecord();
                            Trace.Assert(input[0] == (int) key);
                            // Trace.Assert(input[1] == iSeat);
                            for (int iCard = 0; iCard < 3; ++iCard)
                            {
                                Card card = Card.AllCards[input[2 + iCard]];
                                hands[ConvertSeat(input[1])].Add(card);
                            }
                            WriteSlaveRecord(key, 0);
                        }
                    }
                        // cards have been dealt
                    Deal deal = new Deal(hands.ConvertAll<Hand>(delegate(IList<Card> cards) { return new Hand(cards); }).ToArray(), iFirstBidder);
                    Display(deal);

                        // auction
                    for (int iPlayer = 0; iPlayer < Settings.PlayerCount; ++iPlayer)
                    {
                        input = ReadMasterRecord();
                        Trace.Assert(input[0] == (int) InputKey.BiddingAuction);
                        Bid bid = (input[3] == 0)
                            ? new Bid((BidEnum) input[2])
                            : (Bid) deal.EvaluateBid(game);
                        WriteLine("{0} bids {1}", seats[deal.ActiveSeat], bid);
                        deal.ChooseBid(bid);
                        WriteSlaveRecord(InputKey.BiddingAuction, (int) bid.Level);
                    }
                        // auction is over
                    input = ReadMasterRecord();
                    Trace.Assert(input[0] == (int) InputKey.EndOfBiddingAuction);
                    KeyValuePair<int, Bid> highBid = deal.HighBid;
                    Trace.Assert((input[1] == -1 && highBid.Value.Level == BidEnum.Pass) || (ConvertSeat(input[1]) == highBid.Key));
                    Trace.Assert(input[2] == (int) highBid.Value.Level);
                    WriteSlaveRecord(InputKey.EndOfBiddingAuction, 0);

                        // abandon hand if all players pass
                    if (highBid.Value.Level != BidEnum.Pass)
                    {
                            // play cards until the deal is over
                        while (!deal.IsOver)
                        {
                                // start a trick
                            input = ReadMasterRecord();
                            Trace.Assert(input[0] == (int) InputKey.StartOfTrick);
                            Trace.Assert(ConvertSeat(input[1]) == deal.ActiveSeat);
                            Trace.Assert(input[2] == deal.ActiveTrick);
                            WriteSlaveRecord(InputKey.StartOfTrick, 0);

                                // play a card
                            for (int iPlayer = 0; iPlayer < Settings.PlayerCount; ++iPlayer)
                            {
                                input = ReadMasterRecord();
                                Trace.Assert(input[0] == (int) InputKey.CardInTrick);
                                Trace.Assert(ConvertSeat(input[1]) == deal.ActiveSeat);

                                Card card = (input[3] == 0)
                                    ? Card.AllCards[input[2]]
                                    : (Card) deal.Evaluate(game, false);
                                WriteLine("{0} plays {1}", seats[deal.ActiveSeat], card);
                                deal.ChooseCard(card);
                                WriteSlaveRecord(InputKey.CardInTrick, card.GetHashCode());
                            }
                                // end the trick
                            input = ReadMasterRecord();
                            Trace.Assert(input[0] == (int) InputKey.EndOfTrick);
                            WriteSlaveRecord(InputKey.EndOfTrick, 0);
                        }
                            // count points won
                        int[] points;
                        Deal.BidStateEnum bidState = deal.BidState(false, out points);
                        if (bidState == Deal.BidStateEnum.Set)
                            deal.ApplySetback(points);
                        game.Push(points);
                    }
                        // end the deal
                    input = ReadMasterRecord();
                    Trace.Assert(input[0] == (int) InputKey.EndOfHand);
                    Trace.Assert(input[1] == game.Score[1]);
                    Trace.Assert(input[2] == game.Score[0]);
                    WriteSlaveRecord(InputKey.EndOfHand, 0);
                }
                    // end the game
                input = ReadMasterRecord();
                Trace.Assert(input[0] == (int) InputKey.EndOfGame);
                Trace.Assert(input[1] == game.Score[1]);
                Trace.Assert(input[2] == game.Score[0]);
                WriteSlaveRecord(InputKey.EndOfGame, 0);
            }
        }
    }
}
